/**
 * `GdooWebSocket` constructor.
 *
 * @param {Object} opts
 * {
 *  url websocket链接地址
 *  pingTimeout 未收到消息多少秒之后发送ping请求，默认15000毫秒
    pongTimeout  发送ping之后，未收到消息超时时间，默认10000毫秒
    reconnectTimeout 尝试重连的间隔时间，默认2000毫秒
    pingMsg ping 消息值，默认ping
	repeatLimit 重连尝试次数。默认不限制
 * }
 * @api public
 */

var GdooWebSocket = function({
    url, 
    pingTimeout = 15000,
    pongTimeout = 10000,
    reconnectTimeout = 2000,
    pingMsg = 'ping',
    repeatLimit = null
}){
    this.opts = {
        url: url,
        pingTimeout: pingTimeout,
        pongTimeout: pongTimeout,
        reconnectTimeout: reconnectTimeout,
        pingMsg: pingMsg,
        repeatLimit: repeatLimit
    };

    this.ws = null; //websocket实例
    this.repeat = 0;

    // override hook function
    this.onclose = () => {};
    this.onerror = () => {};
    this.onopen = () => {};
    this.onmessage = () => {};
    this.onreconnect = () => {};
    this.onsend = () => {};
    this.onreconnectCountDown = () => {};
    
    this.createWebSocket();
}

GdooWebSocket.prototype.createWebSocket = function() {
    try {
        this.ws = new WebSocket(this.opts.url);
        this.initEventHandle();
    } catch (e) {
        this.reconnect();
        throw e;
    }     
};

GdooWebSocket.prototype.initEventHandle = function() {
    this.ws.onclose = (e) => {
        this.onclose(e);
        this.reconnect();
    };
    this.ws.onerror = (e) => {
        this.onerror(e);
        this.reconnect();
    };
    this.ws.onopen = () => {
        this.repeat = 0;
        this.onopen();
        //心跳检测重置
        this.heartCheck();
    };
    this.ws.onmessage = (event) => {
        this.onmessage(event);
        //如果获取到消息，心跳检测重置
        //拿到任何消息都说明当前连接是正常的
        this.heartCheck();
    };
};

GdooWebSocket.prototype.reconnect = function() {
    if (this.opts.repeatLimit > 0 && this.opts.repeatLimit <= this.repeat) return; //limit repeat the number
    if (this.lockReconnect || this.forbidReconnect) return;
    this.lockReconnect = true;
    this.repeat++; //必须在lockReconnect之后，避免进行无效计数
    
    this.onreconnect();

    // 倒计时重连
    this.countDown = options.reconnectTimeout / 1000;
    this.reconnectCountDown();

    // 没连接上会一直重连，设置延迟避免请求过多
    setTimeout(() => {
        this.createWebSocket();
        this.lockReconnect = false;
    }, this.opts.reconnectTimeout);
};

GdooWebSocket.prototype.send = function(msg) {
    this.ws.send(msg);
	this.onsend(msg);
};

//心跳检测
GdooWebSocket.prototype.heartCheck = function() {
    this.heartReset();
    this.heartStart();
};

GdooWebSocket.prototype.heartStart = function() {
    // 不再重连就不再执行心跳
    if (this.forbidReconnect) return;

    this.pingTimeoutId = setTimeout(() => {
        // 这里发送一个心跳，后端收到后，返回一个心跳消息，
        // onmessage拿到返回的心跳就说明连接正常
        this.ws.send(this.opts.pingMsg);
        this.onsend(this.opts.pingMsg);
        
        // 如果超过一定时间还没重置，说明后端主动断开了
        this.pongTimeoutId = setTimeout(() => {
            // 如果onclose会执行reconnect，我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
            this.ws.close();
        }, this.opts.pongTimeout);

    }, this.opts.pingTimeout);
};

GdooWebSocket.prototype.reconnectCountDown = function() {
    this.onreconnectCountDown(this.countDown);
    if (this.countDown > 0) {
        this.countDownTimeoutId = setTimeout(() => {
            this.reconnectCountDown();
        }, 1000);
    }
    this.countDown--;
}

GdooWebSocket.prototype.heartReset = function() {
    clearTimeout(this.pingTimeoutId);
    clearTimeout(this.pongTimeoutId);
    clearTimeout(this.countDownTimeoutId);
};

GdooWebSocket.prototype.close = function() {
    // 如果手动关闭连接，不再重连
    this.forbidReconnect = true;
    this.heartReset();
    this.ws.close();
};